"""
数据缓存

提供高效的数据缓存和检索功能。
"""

from typing import Dict, List, Any, Optional, Union, Tuple
from datetime import datetime, timedelta
from dataclasses import dataclass
from enum import Enum
import asyncio
import json
import pickle
from abc import ABC, abstractmethod


class CacheStrategy(Enum):
    """缓存策略"""
    LRU = "lru"           # 最近最少使用
    LFU = "lfu"           # 最少使用频率
    FIFO = "fifo"         # 先进先出
    TTL = "ttl"           # 生存时间
    WRITE_THROUGH = "write_through"    # 写穿透
    WRITE_BACK = "write_back"          # 写回


class CacheLevel(Enum):
    """缓存级别"""
    MEMORY = "memory"     # 内存缓存
    REDIS = "redis"       # Redis缓存
    DISK = "disk"         # 磁盘缓存
    HYBRID = "hybrid"     # 混合缓存


@dataclass
class CacheEntry:
    """缓存条目"""
    key: str
    value: Any
    created_at: datetime
    last_accessed: datetime
    access_count: int
    ttl: Optional[timedelta] = None
    size: int = 0
    metadata: Optional[Dict[str, Any]] = None
    
    def __post_init__(self):
        if self.created_at is None:
            self.created_at = datetime.now()
        if self.last_accessed is None:
            self.last_accessed = self.created_at
    
    @property
    def is_expired(self) -> bool:
        """检查是否过期"""
        if self.ttl is None:
            return False
        return datetime.now() > self.created_at + self.ttl
    
    @property
    def age(self) -> timedelta:
        """获取缓存年龄"""
        return datetime.now() - self.created_at
    
    def touch(self) -> None:
        """更新访问时间和计数"""
        self.last_accessed = datetime.now()
        self.access_count += 1


@dataclass
class CacheStats:
    """缓存统计"""
    total_requests: int = 0
    cache_hits: int = 0
    cache_misses: int = 0
    evictions: int = 0
    total_size: int = 0
    entry_count: int = 0
    
    @property
    def hit_rate(self) -> float:
        """命中率"""
        if self.total_requests == 0:
            return 0.0
        return self.cache_hits / self.total_requests
    
    @property
    def miss_rate(self) -> float:
        """未命中率"""
        return 1.0 - self.hit_rate


class CacheBackend(ABC):
    """缓存后端基类"""
    
    def __init__(self, name: str):
        """
        初始化缓存后端
        
        Args:
            name: 后端名称
        """
        self.name = name
        self._initialized = False
    
    @abstractmethod
    async def initialize(self) -> None:
        """初始化缓存后端"""
        pass
    
    @abstractmethod
    async def get(self, key: str) -> Optional[Any]:
        """获取缓存值"""
        pass
    
    @abstractmethod
    async def set(self, key: str, value: Any, ttl: Optional[timedelta] = None) -> bool:
        """设置缓存值"""
        pass
    
    @abstractmethod
    async def delete(self, key: str) -> bool:
        """删除缓存值"""
        pass
    
    @abstractmethod
    async def exists(self, key: str) -> bool:
        """检查键是否存在"""
        pass
    
    @abstractmethod
    async def clear(self) -> bool:
        """清空缓存"""
        pass
    
    @abstractmethod
    async def get_stats(self) -> Dict[str, Any]:
        """获取统计信息"""
        pass


class MemoryCache(CacheBackend):
    """内存缓存"""
    
    def __init__(self, max_size: int = 1000, strategy: CacheStrategy = CacheStrategy.LRU):
        """
        初始化内存缓存
        
        Args:
            max_size: 最大缓存条目数
            strategy: 缓存策略
        """
        super().__init__("memory")
        self.max_size = max_size
        self.strategy = strategy
        self._cache: Dict[str, CacheEntry] = {}
        self._stats = CacheStats()
        self._lock = asyncio.Lock()
    
    async def initialize(self) -> None:
        """初始化内存缓存"""
        self._initialized = True
    
    async def get(self, key: str) -> Optional[Any]:
        """
        获取缓存值
        
        Args:
            key: 缓存键
            
        Returns:
            缓存值或None
        """
        async with self._lock:
            self._stats.total_requests += 1
            
            if key not in self._cache:
                self._stats.cache_misses += 1
                return None
            
            entry = self._cache[key]
            
            # 检查是否过期
            if entry.is_expired:
                del self._cache[key]
                self._stats.cache_misses += 1
                return None
            
            # 更新访问信息
            entry.touch()
            self._stats.cache_hits += 1
            
            return entry.value
    
    async def set(self, key: str, value: Any, ttl: Optional[timedelta] = None) -> bool:
        """
        设置缓存值
        
        Args:
            key: 缓存键
            value: 缓存值
            ttl: 生存时间
            
        Returns:
            是否设置成功
        """
        async with self._lock:
            # 检查是否需要淘汰
            if len(self._cache) >= self.max_size and key not in self._cache:
                await self._evict()
            
            # 计算大小
            size = self._calculate_size(value)
            
            # 创建缓存条目
            entry = CacheEntry(
                key=key,
                value=value,
                created_at=datetime.now(),
                last_accessed=datetime.now(),
                access_count=1,
                ttl=ttl,
                size=size
            )
            
            self._cache[key] = entry
            self._stats.entry_count = len(self._cache)
            self._stats.total_size += size
            
            return True
    
    async def delete(self, key: str) -> bool:
        """
        删除缓存值
        
        Args:
            key: 缓存键
            
        Returns:
            是否删除成功
        """
        async with self._lock:
            if key in self._cache:
                entry = self._cache[key]
                self._stats.total_size -= entry.size
                del self._cache[key]
                self._stats.entry_count = len(self._cache)
                return True
            return False
    
    async def exists(self, key: str) -> bool:
        """
        检查键是否存在
        
        Args:
            key: 缓存键
            
        Returns:
            是否存在
        """
        async with self._lock:
            if key not in self._cache:
                return False
            
            entry = self._cache[key]
            if entry.is_expired:
                del self._cache[key]
                return False
            
            return True
    
    async def clear(self) -> bool:
        """
        清空缓存
        
        Returns:
            是否清空成功
        """
        async with self._lock:
            self._cache.clear()
            self._stats = CacheStats()
            return True
    
    async def get_stats(self) -> Dict[str, Any]:
        """
        获取统计信息
        
        Returns:
            统计信息字典
        """
        async with self._lock:
            return {
                "backend": self.name,
                "strategy": self.strategy.value,
                "max_size": self.max_size,
                "current_size": len(self._cache),
                "total_requests": self._stats.total_requests,
                "cache_hits": self._stats.cache_hits,
                "cache_misses": self._stats.cache_misses,
                "hit_rate": self._stats.hit_rate,
                "evictions": self._stats.evictions,
                "total_memory": self._stats.total_size
            }
    
    async def _evict(self) -> None:
        """根据策略淘汰缓存条目"""
        if not self._cache:
            return
        
        if self.strategy == CacheStrategy.LRU:
            # 淘汰最近最少使用的条目
            oldest_key = min(self._cache.keys(), key=lambda k: self._cache[k].last_accessed)
        elif self.strategy == CacheStrategy.LFU:
            # 淘汰使用频率最低的条目
            oldest_key = min(self._cache.keys(), key=lambda k: self._cache[k].access_count)
        elif self.strategy == CacheStrategy.FIFO:
            # 淘汰最早创建的条目
            oldest_key = min(self._cache.keys(), key=lambda k: self._cache[k].created_at)
        else:
            # 默认使用LRU
            oldest_key = min(self._cache.keys(), key=lambda k: self._cache[k].last_accessed)
        
        entry = self._cache[oldest_key]
        self._stats.total_size -= entry.size
        del self._cache[oldest_key]
        self._stats.evictions += 1
    
    def _calculate_size(self, value: Any) -> int:
        """
        计算值的大小
        
        Args:
            value: 要计算大小的值
            
        Returns:
            大小（字节）
        """
        try:
            return len(pickle.dumps(value))
        except Exception:
            return len(str(value).encode('utf-8'))


class RedisCache(CacheBackend):
    """Redis缓存"""
    
    def __init__(self, redis_url: str = "redis://localhost:6379", prefix: str = "aiquant:"):
        """
        初始化Redis缓存
        
        Args:
            redis_url: Redis连接URL
            prefix: 键前缀
        """
        super().__init__("redis")
        self.redis_url = redis_url
        self.prefix = prefix
        self._redis = None
        self._stats = CacheStats()
    
    async def initialize(self) -> None:
        """初始化Redis连接"""
        try:
            from ..config.redis_config import get_redis_client
            self._redis = await get_redis_client()
            self._initialized = True
        except Exception as e:
            raise ConnectionError(f"Failed to initialize Redis cache: {e}")
    
    async def get(self, key: str) -> Optional[Any]:
        """
        获取缓存值
        
        Args:
            key: 缓存键
            
        Returns:
            缓存值或None
        """
        if not self._initialized:
            await self.initialize()
        
        self._stats.total_requests += 1
        
        try:
            full_key = f"{self.prefix}{key}"
            data = await self._redis.get(full_key)
            
            if data is None:
                self._stats.cache_misses += 1
                return None
            
            # 反序列化数据
            value = pickle.loads(data)
            self._stats.cache_hits += 1
            
            return value
        except Exception:
            self._stats.cache_misses += 1
            return None
    
    async def set(self, key: str, value: Any, ttl: Optional[timedelta] = None) -> bool:
        """
        设置缓存值
        
        Args:
            key: 缓存键
            value: 缓存值
            ttl: 生存时间
            
        Returns:
            是否设置成功
        """
        if not self._initialized:
            await self.initialize()
        
        try:
            full_key = f"{self.prefix}{key}"
            data = pickle.dumps(value)
            
            if ttl:
                await self._redis.setex(full_key, int(ttl.total_seconds()), data)
            else:
                await self._redis.set(full_key, data)
            
            return True
        except Exception:
            return False
    
    async def delete(self, key: str) -> bool:
        """
        删除缓存值
        
        Args:
            key: 缓存键
            
        Returns:
            是否删除成功
        """
        if not self._initialized:
            await self.initialize()
        
        try:
            full_key = f"{self.prefix}{key}"
            result = await self._redis.delete(full_key)
            return result > 0
        except Exception:
            return False
    
    async def exists(self, key: str) -> bool:
        """
        检查键是否存在
        
        Args:
            key: 缓存键
            
        Returns:
            是否存在
        """
        if not self._initialized:
            await self.initialize()
        
        try:
            full_key = f"{self.prefix}{key}"
            result = await self._redis.exists(full_key)
            return result > 0
        except Exception:
            return False
    
    async def clear(self) -> bool:
        """
        清空缓存
        
        Returns:
            是否清空成功
        """
        if not self._initialized:
            await self.initialize()
        
        try:
            # 删除所有带前缀的键
            pattern = f"{self.prefix}*"
            keys = await self._redis.keys(pattern)
            if keys:
                await self._redis.delete(*keys)
            return True
        except Exception:
            return False
    
    async def get_stats(self) -> Dict[str, Any]:
        """
        获取统计信息
        
        Returns:
            统计信息字典
        """
        try:
            info = await self._redis.info("memory")
            return {
                "backend": self.name,
                "redis_url": self.redis_url,
                "prefix": self.prefix,
                "total_requests": self._stats.total_requests,
                "cache_hits": self._stats.cache_hits,
                "cache_misses": self._stats.cache_misses,
                "hit_rate": self._stats.hit_rate,
                "redis_memory": info.get("used_memory", 0),
                "redis_memory_human": info.get("used_memory_human", "0B")
            }
        except Exception:
            return {
                "backend": self.name,
                "error": "Failed to get Redis stats"
            }


class HybridCache(CacheBackend):
    """混合缓存（内存+Redis）"""
    
    def __init__(self, memory_cache: MemoryCache, redis_cache: RedisCache):
        """
        初始化混合缓存
        
        Args:
            memory_cache: 内存缓存
            redis_cache: Redis缓存
        """
        super().__init__("hybrid")
        self.memory_cache = memory_cache
        self.redis_cache = redis_cache
    
    async def initialize(self) -> None:
        """初始化混合缓存"""
        await self.memory_cache.initialize()
        await self.redis_cache.initialize()
        self._initialized = True
    
    async def get(self, key: str) -> Optional[Any]:
        """
        获取缓存值（先查内存，再查Redis）
        
        Args:
            key: 缓存键
            
        Returns:
            缓存值或None
        """
        # 先查内存缓存
        value = await self.memory_cache.get(key)
        if value is not None:
            return value
        
        # 再查Redis缓存
        value = await self.redis_cache.get(key)
        if value is not None:
            # 将值放入内存缓存
            await self.memory_cache.set(key, value)
        
        return value
    
    async def set(self, key: str, value: Any, ttl: Optional[timedelta] = None) -> bool:
        """
        设置缓存值（同时设置内存和Redis）
        
        Args:
            key: 缓存键
            value: 缓存值
            ttl: 生存时间
            
        Returns:
            是否设置成功
        """
        # 同时设置两个缓存
        memory_result = await self.memory_cache.set(key, value, ttl)
        redis_result = await self.redis_cache.set(key, value, ttl)
        
        return memory_result and redis_result
    
    async def delete(self, key: str) -> bool:
        """
        删除缓存值（同时删除内存和Redis）
        
        Args:
            key: 缓存键
            
        Returns:
            是否删除成功
        """
        memory_result = await self.memory_cache.delete(key)
        redis_result = await self.redis_cache.delete(key)
        
        return memory_result or redis_result
    
    async def exists(self, key: str) -> bool:
        """
        检查键是否存在（检查内存或Redis）
        
        Args:
            key: 缓存键
            
        Returns:
            是否存在
        """
        memory_exists = await self.memory_cache.exists(key)
        if memory_exists:
            return True
        
        return await self.redis_cache.exists(key)
    
    async def clear(self) -> bool:
        """
        清空缓存（同时清空内存和Redis）
        
        Returns:
            是否清空成功
        """
        memory_result = await self.memory_cache.clear()
        redis_result = await self.redis_cache.clear()
        
        return memory_result and redis_result
    
    async def get_stats(self) -> Dict[str, Any]:
        """
        获取统计信息
        
        Returns:
            统计信息字典
        """
        memory_stats = await self.memory_cache.get_stats()
        redis_stats = await self.redis_cache.get_stats()
        
        return {
            "backend": self.name,
            "memory_cache": memory_stats,
            "redis_cache": redis_stats
        }


class DataCache:
    """
    数据缓存管理器
    
    提供统一的缓存接口和多级缓存支持。
    """
    
    def __init__(self, level: CacheLevel = CacheLevel.MEMORY):
        """
        初始化数据缓存
        
        Args:
            level: 缓存级别
        """
        self.level = level
        self._backend: Optional[CacheBackend] = None
        self._initialized = False
    
    async def initialize(self) -> None:
        """初始化缓存后端"""
        if self.level == CacheLevel.MEMORY:
            self._backend = MemoryCache(max_size=1000, strategy=CacheStrategy.LRU)
        elif self.level == CacheLevel.REDIS:
            self._backend = RedisCache()
        elif self.level == CacheLevel.HYBRID:
            memory_cache = MemoryCache(max_size=500, strategy=CacheStrategy.LRU)
            redis_cache = RedisCache()
            self._backend = HybridCache(memory_cache, redis_cache)
        else:
            # 默认使用内存缓存
            self._backend = MemoryCache()
        
        await self._backend.initialize()
        self._initialized = True
    
    async def get_market_data(
        self,
        symbol: str,
        date: datetime,
        fields: List[str]
    ) -> Optional[Dict[str, Any]]:
        """
        获取市场数据缓存
        
        Args:
            symbol: 股票代码
            date: 日期
            fields: 字段列表
            
        Returns:
            缓存的市场数据或None
        """
        if not self._initialized:
            await self.initialize()
        
        key = self._generate_market_data_key(symbol, date, fields)
        return await self._backend.get(key)
    
    async def set_market_data(
        self,
        symbol: str,
        date: datetime,
        fields: List[str],
        data: Dict[str, Any],
        ttl: Optional[timedelta] = None
    ) -> bool:
        """
        设置市场数据缓存
        
        Args:
            symbol: 股票代码
            date: 日期
            fields: 字段列表
            data: 市场数据
            ttl: 生存时间
            
        Returns:
            是否设置成功
        """
        if not self._initialized:
            await self.initialize()
        
        key = self._generate_market_data_key(symbol, date, fields)
        return await self._backend.set(key, data, ttl)
    
    async def get_fundamental_data(
        self,
        symbol: str,
        period: str,
        metrics: List[str]
    ) -> Optional[Dict[str, Any]]:
        """
        获取基本面数据缓存
        
        Args:
            symbol: 股票代码
            period: 报告期间
            metrics: 指标列表
            
        Returns:
            缓存的基本面数据或None
        """
        if not self._initialized:
            await self.initialize()
        
        key = self._generate_fundamental_data_key(symbol, period, metrics)
        return await self._backend.get(key)
    
    async def set_fundamental_data(
        self,
        symbol: str,
        period: str,
        metrics: List[str],
        data: Dict[str, Any],
        ttl: Optional[timedelta] = None
    ) -> bool:
        """
        设置基本面数据缓存
        
        Args:
            symbol: 股票代码
            period: 报告期间
            metrics: 指标列表
            data: 基本面数据
            ttl: 生存时间
            
        Returns:
            是否设置成功
        """
        if not self._initialized:
            await self.initialize()
        
        key = self._generate_fundamental_data_key(symbol, period, metrics)
        return await self._backend.set(key, data, ttl)
    
    async def get_analysis_result(
        self,
        analysis_type: str,
        parameters: Dict[str, Any]
    ) -> Optional[Dict[str, Any]]:
        """
        获取分析结果缓存
        
        Args:
            analysis_type: 分析类型
            parameters: 分析参数
            
        Returns:
            缓存的分析结果或None
        """
        if not self._initialized:
            await self.initialize()
        
        key = self._generate_analysis_key(analysis_type, parameters)
        return await self._backend.get(key)
    
    async def set_analysis_result(
        self,
        analysis_type: str,
        parameters: Dict[str, Any],
        result: Dict[str, Any],
        ttl: Optional[timedelta] = None
    ) -> bool:
        """
        设置分析结果缓存
        
        Args:
            analysis_type: 分析类型
            parameters: 分析参数
            result: 分析结果
            ttl: 生存时间
            
        Returns:
            是否设置成功
        """
        if not self._initialized:
            await self.initialize()
        
        key = self._generate_analysis_key(analysis_type, parameters)
        return await self._backend.set(key, result, ttl)
    
    async def invalidate_symbol_data(self, symbol: str) -> bool:
        """
        失效某个股票的所有缓存数据
        
        Args:
            symbol: 股票代码
            
        Returns:
            是否成功失效
        """
        if not self._initialized:
            await self.initialize()
        
        # 这里应该实现模式匹配删除
        # 暂时返回True作为框架
        return True
    
    async def get_cache_stats(self) -> Dict[str, Any]:
        """
        获取缓存统计信息
        
        Returns:
            统计信息字典
        """
        if not self._initialized:
            await self.initialize()
        
        return await self._backend.get_stats()
    
    async def clear_cache(self) -> bool:
        """
        清空所有缓存
        
        Returns:
            是否清空成功
        """
        if not self._initialized:
            await self.initialize()
        
        return await self._backend.clear()
    
    def _generate_market_data_key(self, symbol: str, date: datetime, fields: List[str]) -> str:
        """生成市场数据缓存键"""
        fields_str = ",".join(sorted(fields))
        date_str = date.strftime("%Y%m%d")
        return f"market_data:{symbol}:{date_str}:{hash(fields_str)}"
    
    def _generate_fundamental_data_key(self, symbol: str, period: str, metrics: List[str]) -> str:
        """生成基本面数据缓存键"""
        metrics_str = ",".join(sorted(metrics))
        return f"fundamental_data:{symbol}:{period}:{hash(metrics_str)}"
    
    def _generate_analysis_key(self, analysis_type: str, parameters: Dict[str, Any]) -> str:
        """生成分析结果缓存键"""
        params_str = json.dumps(parameters, sort_keys=True)
        return f"analysis:{analysis_type}:{hash(params_str)}"